package org.hackystat.devcathlon.event.impl;

import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.devcathlon.event.Event;
import org.hackystat.devcathlon.event.EventException;
import org.hackystat.devcathlon.event.EventScore;
import org.hackystat.devcathlon.match.Match;
import org.hackystat.devcathlon.team.Team;
import org.hackystat.sensorbase.client.SensorBaseClient;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex;
import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef;
import org.hackystat.utilities.tstamp.Tstamp;

/**
 * Rewards points for build before commit.
 * 
 * @author John Zhou
 */
public class BuildBeforeCommit extends Event {

  /** Name of the event. */
  public static final String eventName = "Build Before Commit";
  /** The property key for the build and commit interval. */
  public static final String buildCommitKey = "BuildCommitInterval";
  /** The actual build and commit interval. */
  private int buildCommitInterval;
  /** The property key for the commit streak threshold. */
  public static final String upperThresholdKey = "UpperThreshold";
  /** The actual commit streak threshold. */
  private int streakThreshold;
  /** The property key for the bonus weight. */
  public static final String meetThresholdKey = "MeetThreshold";
  /** The actual bonus weight. */
  private int bonusWeight;
  /** The name of the bonus for having a streak. */
  public static final String bonusName = "Streak Bonus";

  /** Count of number of times points were awarded. */
  private int awardCount;

  /**
   * Constructor for the BuildBeforeCommit class.
   * 
   * @throws EventException If the configuration cannot be created.
   */
  public BuildBeforeCommit() throws EventException {
    super(eventName);
    try {
      buildCommitInterval = Integer.parseInt(configManager.getProperty(eventName, buildCommitKey));
      streakThreshold = Integer.parseInt(configManager.getProperty(eventName, upperThresholdKey));
      bonusWeight = Integer.parseInt(configManager.getProperty(eventName, meetThresholdKey));
      awardCount = 0;
    }
    catch (Exception e) {
      throw new EventException("Error configuring properties", e);
    }
  }

  /**
   * Returns the total number of points for this event.
   * 
   * @param score The event score.
   * @return The total points for this event given the current weights.
   */
  @Override
  public int computePoints(EventScore score) {
    return ((score.getBaseScore() * this.baseScoreWeight) + 
        (score.getBonusScore(bonusName) * this.bonusWeight));
  }

  /**
   * Compute score for BuildBeforeCommit.
   * 
   * @param match The match of interest.
   * @param team The team of interest.
   * @param timestamp The time at which this event is being run.
   * @return The EventScore.
   * @throws EventException If problems occur while processing this event.
   */
  @Override
  public EventScore computeScore(Match match, Team team, XMLGregorianCalendar timestamp)
      throws EventException {
    EventScore score = new EventScore(timestamp, match, team);
    Project project = match.getProject(team);
    SensorBaseClient client = match.getSensorBaseClient(team);
    String projectName = project.getName();
    String owner = project.getOwner();
    String sdt = "Commit";
    XMLGregorianCalendar endTime = timestamp;
    XMLGregorianCalendar startTime = Tstamp.incrementMinutes(endTime, wakeupInterval * -1);
    SensorDataIndex index;

    this.logger.info(String.format("Getting commit sensor data for match %s and team %s", match
        .getName(), team.getName()));
    try {
      index = client.getProjectSensorData(owner, projectName, startTime, endTime, sdt);
    }
    catch (Exception e) {
      throw new EventException("Error retrieving commit data.", e);
    }
    XMLGregorianCalendar commitTime;
    for (SensorDataRef ref : index.getSensorDataRef()) {
      if (ref.getSensorDataType().equals("Commit")) {
        commitTime = ref.getTimestamp();
        // if there are no commits return default score
        if (commitTime == null) {
          return score;
        }
        // if there is a build before a commit compute score
        if (isBuildFollowsCommit(client, owner, projectName, commitTime)) {
          score.setBaseScore(score.getBaseScore() + 1);
          awardCount++;
          if (awardCount == streakThreshold) {
            score.addBonusScore(bonusName, 1);
          }
        }
        else {
          awardCount = 0;
          score.setBaseScore(score.getBaseScore() - 1);
        }
      }
    }
    return score;
  }

  /**
   * Returns true if there is a build before a commit. Otherwise it returns false.
   * 
   * @param client The sensorbase client.
   * @param projectName The project of interest.
   * @param owner The owner of interest.
   * @param commitTime The time of the commit.
   * @return A boolean value if there is a Build before a commit.
   * @throws EventException If problems occur while processing this event.
   */
  private boolean isBuildFollowsCommit(SensorBaseClient client, String owner, String projectName,
      XMLGregorianCalendar commitTime) throws EventException {
    XMLGregorianCalendar buildTime = Tstamp.incrementMinutes(commitTime, -buildCommitInterval);
    SensorDataIndex index;
    String sdt = "Build";
    try {
      // testing keeps throwing exception
      index = client.getProjectSensorData(owner, projectName, buildTime, commitTime, sdt);
    }
    catch (Exception e) {
      throw new EventException("Error retrieving build data.", e);
    }
    return index.getSensorDataRef().size() != 0;
  }
}
